package com.ymx.darling.mapper;

import com.ymx.darling.bean.entity.BeanInfo;
import com.ymx.darling.bean.entity.FieldInfo;
import com.ymx.darling.cursor.Cursor;
import com.ymx.darling.dialect.page.Page;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.session.Session;
import com.ymx.darling.session.setting.QuerySetting;
import com.ymx.darling.wrapper.*;
import com.ymx.darling.wrapper.build.DeleteBuilder;
import com.ymx.darling.wrapper.build.InsertBuilder;
import com.ymx.darling.wrapper.build.SelectBuilder;
import com.ymx.darling.wrapper.build.UpdateBuilder;
import com.ymx.darling.bean.sql.SqlStatement;

import java.io.Serializable;
import java.util.*;

/**
 * 默认的EntityMapper实现类
 * @author 爱java的小于
 * @param <T>
 */
public class DefaultEntityMapper<T> implements EntityMapper<T> {
    private final Session session;
    private final BeanInfo beanInfo;

    public DefaultEntityMapper(Session session, BeanInfo beanInfo) {
        this.session = session;
        this.beanInfo = beanInfo;
    }


    /**
     * 返回一个查询构造器
     *
     * @return QueryWrapper
     */
    private QueryWrapper qw(){
        return new QueryWrapper();
    }


    /**
     * 返回一个更新构造器
     * @return UpdateWrapper
     */
    private UpdateWrapper uw(){
        return new UpdateWrapper();
    }


    /**
     *查询结果为List时可以指定wrapper条件为null
     *
     * @param wrapper 条件包装器
     * @return Wrapper
     */
    private QueryWrapper selectListWrapperEqNull(QueryWrapper wrapper){
        if (Objects.isNull(wrapper)) {
            return new QueryWrapper();
        }
        return wrapper;
    }


    /**
     * 将Wrapper转换为SqlStatement
     * @param wrapper Wrapper类型
     * @return 查询语句的封装类
     */
    private SqlStatement getSelectStatement(QueryWrapper wrapper) {
        return new SelectBuilder().buildSelect(wrapper, this.beanInfo);
    }


    private Session getSession(){
        if (this.session == null)
            throw new NullPointerException("实体:"+this+"中Session接口实例为null");
        return this.session;
    }


    @Override
    public T selectOne(QueryWrapper wrapper) {
        return this.selectOneAs(wrapper, this.beanInfo.getModule());
    }


    @Override
    public <E> E selectOneAs(QueryWrapper wrapper, Class<?> eClass) {
        SqlStatement statement = this.getSelectStatement(this.selectListWrapperEqNull(wrapper));
        return this.getSession().selectOne(eClass,statement.getSql(), statement.getParamList().toArray());
    }


    @Override
    public T selectById(Serializable id) {
        return this.selectOne(qw().eq(this.getIdName(),id));
    }


    /**
     * 返回id字段名称
     *
     * @return idName
     * @throws EntityException 如果没有指定id字段则会抛出此异常
     */
    public String getIdName(){
        FieldInfo idFieldInfo = this.beanInfo.getIdColumnInfo();
        if (idFieldInfo == null){
            throw new EntityException("类:"+this.beanInfo.getModule()+"没有指定id字段");
        }
        return idFieldInfo.getColumn();
    }


    @Override
    public List<T> selectBatchIds(List<? extends Serializable> idList) {
        return this.selectList(qw().in(this.getIdName(), idList));
    }


    @Override
    public List<T> selectList(QueryWrapper wrapper) {
        return this.selectListAs(wrapper, this.beanInfo.getModule());
    }


    @Override
    public <E> List<E> selectListAs(QueryWrapper wrapper, Class<?> type) {
        SqlStatement os = this.getSelectStatement(this.selectListWrapperEqNull(wrapper));
        return this.getSession().selectList(type, os.getSql(), os.getParamList().toArray());
    }


    @Override
    public <K> Map<K, T> selectMap(QueryWrapper wrapper, String key) {
        return this.selectMapAs(wrapper, this.beanInfo.getModule(), key);
    }


    @Override
    public <K,E> Map<K,E> selectMapAs(QueryWrapper wrapper, Class<?> module, String key){
        SqlStatement statement = this.getSelectStatement(this.selectListWrapperEqNull(wrapper));
        return this.getSession().selectMap(module, statement.getSql(), key, statement.getParamList().toArray());
    }


    @Override
    public Page<T> selectPage(QueryWrapper wrapper) {
        wrapper = this.selectListWrapperEqNull(wrapper);
        SqlStatement os = this.getSelectStatement(wrapper);
        return this.getSession().selectPage(this.beanInfo.getModule(), os.getSql(), os.getParamList().toArray());
    }


    @Override
    public Integer selectCount(QueryWrapper wrapper) {
        QueryWrapper selectCountWrapper = this.selectListWrapperEqNull(wrapper);
        // 这里进行优化配置 判断配置id字段 那么将id作为查询字段
        FieldInfo idFieldInfo = this.beanInfo.getIdColumnInfo();
        String queryCountColumn = Objects.isNull(idFieldInfo) ? "*" : idFieldInfo.getColumn();
        selectCountWrapper.select(String.format("count(%s)", queryCountColumn));
        return this.selectOneAs(selectCountWrapper, Integer.class);
    }


    @Override
    public Cursor<T> selectCursor(QueryWrapper wrapper, int fetchSize) {
        return this.selectCursor(wrapper, this.beanInfo.getModule(), fetchSize);
    }


    @Override
    public <E> Cursor<E> selectCursor(QueryWrapper wrapper, Class<?> type, int fetchSize) {
        SqlStatement os = this.getSelectStatement(this.selectListWrapperEqNull(wrapper));
        return this.getSession().selectCursor(type, fetchSize, os.getSql(), os.getParamList().toArray());
    }


    @Override
    public Integer insert(T entity) {
        InsertBuilder insertBuilderSplit = new InsertBuilder(this.beanInfo);
        String insertStatement = insertBuilderSplit.splitInsert(entity);

        return this.getSession().update(insertStatement, entity);
    }


    @Override
    public Integer insertBatch(List<T> entityList) {
        int DEFAULT_BATCH_INSERT = 1000;//批量插入的默认值为1000条数据
        return this.insertBatch(entityList, DEFAULT_BATCH_INSERT);
    }


    @Override
    public Integer insertBatch(List<T> entityList, int batchSize) {
        entityList.forEach(Objects::requireNonNull);
        // 设置批量大小
        QuerySetting.setInsertBatchSize(batchSize);
        // 构建批量插入的sql
        InsertBuilder insertBuilderSplit = new InsertBuilder(this.beanInfo);
        String batchInsertStatement = insertBuilderSplit.splitInsert(entityList.toArray());

        return this.getSession().update(batchInsertStatement, entityList);
    }


    @Override
    public Integer deleteById(Serializable id) {
        return this.delete(uw().eq(this.getIdName(),id));
    }

    @Override
    public Integer deleteBatchIds(List<? extends Serializable> ids) {
        return this.delete(uw().in(this.getIdName(),ids));
    }


    @Override
    public Integer delete(UpdateWrapper wrapper) {
        this.updateWrapperEqNull(wrapper);
        SqlStatement os = new DeleteBuilder().splitDelete(wrapper, this.beanInfo);
        return this.getSession().update(os.getSql(), os.getParamList().toArray());
    }


    @Override
    public Integer update(UpdateWrapper wrapper) {
        this.updateWrapperEqNull(wrapper);
        SqlStatement os = new UpdateBuilder().update(wrapper, this.beanInfo);
        return this.getSession().update(os.getSql(), os.getParamList().toArray());
    }

    @Override
    public Integer update(T entity) {
        Objects.requireNonNull(entity);
        SqlStatement os = new UpdateBuilder().update(entity, Objects::nonNull);
        return this.getSession().update(os.getSql(), os.getParamList().toArray());
    }


    @Override
    public Integer updateAll(T entity) {
        SqlStatement os = new UpdateBuilder().update(entity, o -> true);
        return this.getSession().update(os.getSql(), os.getParamList().toArray());
    }


    /**
     * 为UpdateWrapper进行合法性检查
     * 当为null时创建一个新的UpdateWrapper实例返回
     * 否则 直接返回参数实例
     * @param wrapper UpdateWrapper
     */
    private void updateWrapperEqNull(Wrapper<?> wrapper){
        if (wrapper == null) {
            throw new DarlingException("Wrapper条件构造器异常:'UpdateWrapper的实例不能为null'");
        } else {
            if (!(wrapper instanceof UpdateWrapper)){
                throw new DarlingException("Wrapper条件构造器异常:'执行更新数据时请指定Wrapper类型为UpdateWrapper");
            }
        }
    }
}
